﻿namespace ScrumLite.Services
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Transactions;
    using System.Linq;
    using Contracts;
    using System.Security.Permissions;
    using Chestnut.ComplianceChecker.Services;
    using System.Xml;
    using System.Globalization;
    using System.Configuration;

    public class LinqProjectsService : IProjectsService
    {
        ScrumLite.Data.IProjectStore store;
        IProfileService profileService;

        public LinqProjectsService(ScrumLite.Data.IProjectStore store, IProfileService service)
        {
            this.store = store;
            this.profileService = service;
        }

        public LinqProjectsService(ScrumLite.Data.IProjectStore store)
            : this(store, new ActiveDirectoryProfileService())
        {
        }

        public LinqProjectsService()
            : this(new ScrumLite.Data.ProjectStore(), new ActiveDirectoryProfileService())
        {
        }

        public Project[] GetProjects()
        {
            List<Project> projects = new List<ScrumLite.Data.Project>(
                   this.store.GetProjects()).
               ConvertAll<Project>(delegate(ScrumLite.Data.Project proj)
               {
                   return new Project()
                   {
                       Id = proj.id,
                       Name = proj.name,
                       StartsOn = proj.startsOn,
                       EndsOn = proj.endsOn,
                       Group = proj.security_group,
                       RepositoryUri = proj.uri_repository,
                       ProjectSpecificMetricsUri = proj.uri_projectSpecificMetrics                  
                   };
               });

            return projects.ToArray();
        }

        public string GetProjectName(string projectId)
        {
            ScrumLite.Data.Project proj = this.store.GetProject(new Guid(projectId));
         
            return proj.name;
        }

        public string GetProjectRepository(string projectId)
        {
            ScrumLite.Data.Project proj = this.store.GetProject(new Guid(projectId));

            return proj.uri_repository;
        }

        public void Add(Project project)
        {
            using (TransactionScope scope = new TransactionScope())
            {
                ScrumLite.Data.Project proj = new ScrumLite.Data.Project()
                {
                    id = project.Id,
                    name = project.Name,
                    startsOn = project.StartsOn,
                    endsOn = project.EndsOn,
                    security_group = project.Group,
                    uri_repository = project.RepositoryUri,
                    uri_projectSpecificMetrics = project.ProjectSpecificMetricsUri
                    //TODO: user_ownedBy                      
                };
                this.store.Add(proj);
                scope.Complete();
            }
        }

        [SecurityPermission(SecurityAction.LinkDemand)]
        public PersonAssignation[] GetPersonsInProject(string projectId)
        {
            Dictionary<ScrumLite.Data.ProjectPerson, List<ScrumLite.Data.Role>> projectPersons = this.store.GetPeople(new Guid(projectId));

            if (projectPersons.Count() > 0)
            {
                List<PersonAssignation> persons = new List<PersonAssignation>();
                Collection<Person> allPeople = profileService.GetPeople();
                ScrumLite.Data.Project project = this.store.GetProject(new Guid(projectId));

                foreach (ScrumLite.Data.ProjectPerson person in projectPersons.Keys)
                {
                    PersonAssignation assignation = new PersonAssignation();
                    assignation.IsInternal = person.isInternal;
                    assignation.Project = new Project() { EndsOn = project.endsOn, Id = project.id, Name = project.name, StartsOn = project.startsOn, Group = project.security_group, RepositoryUri = project.uri_repository };
                    assignation.Person = allPeople.FirstOrDefault(p => p.Id == person.samAccountName);
                    assignation.Roles = Map(projectPersons[person]);

                    persons.Add(assignation);
                }
                return persons.ToArray();
            }
            else
            {
                return null;
            }
        }

        private static ProjectRole[] Map(List<ScrumLite.Data.Role> roles)
        {
            return (from r in roles
                    select new ProjectRole
                    {
                        Id = r.id,
                        Name = r.name
                    }).ToArray();
        }

        public string GetUriProjectSpecificMetrics(string projectId)
        {
            ScrumLite.Data.Project proj = this.store.GetProject(new Guid(projectId));

            return proj.uri_projectSpecificMetrics;
        }

        public Project GetProject(string projectId)
        {
            ScrumLite.Data.Project proj = this.store.GetProject(new Guid(projectId));
            if (proj == null)
            {
                throw new ArgumentNullException("projectId");
            }

            return new Project() { 
                Id = proj.id, 
                Group = proj.security_group, 
                Name = proj.name, 
                EndsOn = proj.endsOn, 
                StartsOn = proj.startsOn, 
                ProjectSpecificMetricsUri = proj.uri_projectSpecificMetrics, 
                RepositoryUri = proj.uri_repository 
            };
        }

        public Project GetProjectByName(string projectName)
        {
            ScrumLite.Data.Project proj = this.store.GetProjectByName(projectName);
            if (proj == null)
            {
                throw new ArgumentNullException("projectName");
            }

            return new Project()
            {
                Id = proj.id,
                Group = proj.security_group,
                Name = proj.name,
                EndsOn = proj.endsOn,
                StartsOn = proj.startsOn,
                ProjectSpecificMetricsUri = proj.uri_projectSpecificMetrics,
                RepositoryUri = proj.uri_repository
            };
        }

        public void AddPersonToProject(string projectId, TeamMember person)
        {
            using (TransactionScope scope = new TransactionScope())
            {
                ScrumLite.Data.ProjectPerson projectperson = new ScrumLite.Data.ProjectPerson()
                {
                    id = Guid.NewGuid(),
                    fk_project = new Guid(projectId),
                    isInternal = person.IsInternal,
                    samAccountName = person.PersonId
                };
                this.store.AddPerson(projectperson);

                if (person.RoleIds!=null)
                    foreach (string roleid in person.RoleIds)
                    {
                        ScrumLite.Data.ProjectPersonRole role = new ScrumLite.Data.ProjectPersonRole()
                        {
                            fk_projectPerson = projectperson.id,
                            fk_role = new Guid(roleid)
                        };

                        this.store.AddPersonRole(role);
                    }

                scope.Complete();
            }
        }

        public void RemovePersonFromProject(string projectId, string personId)
        {
            using (TransactionScope scope = new TransactionScope())
            {
                store.RemovePerson(new Guid(projectId), personId);

                scope.Complete();
            }
        }

        public CheckStatus[] CheckCompliance(string projectId)
        {
            CheckStatus status;
            Dictionary<string, string[]> checkList = store.CheckProjectCompliance(new Guid(projectId));
            Collection<CheckStatus> statusList = new Collection<CheckStatus>();
            foreach (string key in checkList.Keys)
            {
                if (checkList[key] != null)
                {
                    status = new CheckStatus(new Collection<string>(checkList[key]));
                    status.Status = (checkList[key].Count() == 0);
                }
                else
                {
                    status = new CheckStatus();
                    status.Status = true;
                }
                status.PropertyChecked = key;

                statusList.Add(status);
            }

            Project project = this.GetProject(projectId);
            Initialize();
            status = CheckSVNRepository(project);

            statusList.Add(status);

            return statusList.ToArray();
        }

        private static CheckStatus CheckSVNRepository(Project project)
        {
            CheckStatus status;
            //Add svn repository
            WebFileSystemProvider provider = new WebFileSystemProvider(project.RepositoryUri + "/managed/process/");
            ComplianceCheckerService service = new ComplianceCheckerService(provider);

            //Exclusions
            Collection<DateTime> exclusions = new Collection<DateTime>();
            string[] dateList = ConfigurationManager.AppSettings["Dates"].Split(',');

            foreach (string value in dateList)
            {
                DateTime date = DateTime.Parse(value);
                exclusions.Add(date);
            }

            DateTime finalDate = (project.EndsOn.HasValue && project.EndsOn.Value.Date < DateTime.Now.Date ? project.EndsOn.Value : DateTime.Now.Date);

            string[] missingFiles = service.DoCheck(project.StartsOn.Date, finalDate, DateTime.Now.Date, exclusions.ToArray());

            if (missingFiles != null)
            {
                status = new CheckStatus(new Collection<string>(missingFiles));
                status.Status = (missingFiles.Count() == 0);
            }
            else
            {
                status = new CheckStatus();
                status.Status = true;
            }
            status.PropertyChecked = "SVN Repository has all the required records";

            return status;
        }

        private void Initialize()
        {
            HistoryName history;
            ItemsNamingHelper.PropertyNames.Clear();

            Dictionary<string, Collection<ScrumLite.Data.ComplianceChecker>> properties = store.GetComplianceCheckerProperties();

            foreach (string property in properties.Keys)
            {
                PropertyName mappedProperty = Map(property);
                if (ItemsNamingHelper.PropertyNames.Keys.Contains(mappedProperty))
                {
                    history = ItemsNamingHelper.PropertyNames[mappedProperty];
                }
                else
                {
                    history = new HistoryName();
                }

                foreach (ScrumLite.Data.ComplianceChecker check in properties[property])
                {
                    history.History.Add(check.dateBegin, check.value);
                }

                if (ItemsNamingHelper.PropertyNames.Keys.Contains(mappedProperty))
                {
                    ItemsNamingHelper.PropertyNames[mappedProperty] = history;
                }
                else
                {
                    ItemsNamingHelper.PropertyNames.Add(mappedProperty, history);
                }
            }
        }

        private static PropertyName Map(string property)
        {
            switch (property.Trim())
            {
                case "GenericProcessFolder": return PropertyName.GenericProcessFolder;
                case "ReviewsFolderName": return PropertyName.ReviewsFolderName;
                case "SOIFileName": return PropertyName.SOIFileName;
                case "EOIFileName": return PropertyName.EOIFileName;
                case "ReviewFileName": return PropertyName.ReviewFileName;
                case "EODFileName": return PropertyName.EODFileName;
                case "SODFileName": return PropertyName.SODFileName;
                case "WeekFolderName": return PropertyName.WeekFolderName;
                case "WeeklyProcessLocation": return PropertyName.WeeklyProcessLocation;
                case "DailyProcessLocation": return PropertyName.DailyProcessLocation;
                case "ReviewProcessLocation": return PropertyName.ReviewProcessLocation;
                case "DevelopmentProcessLocation": return PropertyName.DevelopmentProcessLocation;
                case "DevelopmentFolderName": return PropertyName.DevelopmentFolderName;
                case "UseCaseDiagramFileName": return PropertyName.UseCaseDiagramFileName;
                case "UserStoriesFileName": return PropertyName.UserStoriesFileName;
                case "DeploymentDiagramFileName": return PropertyName.DeploymentDiagramFileName;
                case "ArchitectureDiagramFileName": return PropertyName.ArchitectureDiagramFileName;
                case "DevelopmentEnvironmentFileName": return PropertyName.DevelopmentEnvironmentFileName;
                default: return PropertyName.GenericProcessFolder;
            }
        }

        public void Update(Project project)
        {
            using (TransactionScope scope = new TransactionScope())
            {
                ScrumLite.Data.Project proj = new ScrumLite.Data.Project()
                {
                    id = project.Id,
                    name = project.Name,
                    startsOn = project.StartsOn,
                    endsOn = project.EndsOn,
                    security_group = project.Group,
                    uri_repository = project.RepositoryUri,
                    uri_projectSpecificMetrics = project.ProjectSpecificMetricsUri
                };
                this.store.Update(proj);
                scope.Complete();
            }
        }
    }
}
